package vote.ai.factory;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.github.difflib.DiffUtils;
import com.github.difflib.patch.AbstractDelta;
import com.github.difflib.patch.Chunk;
import com.github.difflib.patch.Patch;
import com.google.common.base.Splitter;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import vote.ai.config.AiCorrectionConfig;
import vote.ai.domain.entity.CorrectTaskDetails;
import vote.ai.domain.entity.CorrectTaskDetailsResp;
import vote.ai.domain.entity.CorrectionDict;
import vote.ai.domain.entity.TextCorrectTask;
import vote.ai.domain.enums.CorrectState;
import vote.ai.domain.enums.CorrectionModel;
import vote.ai.domain.vo.CorrectTaskResp;
import vote.ai.domain.vo.CreateCorrectTaskResp;
import vote.ai.domain.vo.TextCorrectionMatchResp;
import vote.ai.mapper.TextCorrectTaskMapper;
import vote.ai.service.AiCorrectionService;
import vote.ai.service.CorrectTaskDetailsService;
import vote.ai.service.CorrectionCharLimitService;
import vote.ai.service.CorrectionDictService;
import vote.ai.utils.CorrectionUtil;
import vote.ai.utils.SmartPageUtil;
import vote.ai.utils.TextCorrectMatch;
import vote.util.BusinessException;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Description:
 * Author:han
 * Date: 2024-05-21
 */
@Slf4j
@Service
public class CorrectionFactory {
    @Resource
    private TextCorrectTaskMapper textCorrectTaskMapper;
    @Resource
    private CorrectTaskDetailsService correctTaskDetailsService;
    @Resource
    private CorrectionCharLimitService userCorrectionCharLimitService;
    @Resource(name = "correctAsyncServiceExecutor")
    private ThreadPoolTaskExecutor correctExecutor;
    @Resource
    private AiServiceFactory aiServiceFactory;
    @Resource
    private CorrectionDictService correctionDictService;
    @Resource
    private AiCorrectionConfig aiCorrectionConfig;

    /**
     * 创建纠错任务
     *
     * @param text 纠错文本
     * @return 创建纠错任务响应
     */
    @Transactional(rollbackFor = Exception.class)
    public CreateCorrectTaskResp createCorrectTask(String text) {
        log.info("添加纠错任务");
        TextCorrectTask textCorrectTask = new TextCorrectTask();
        text = text.replaceAll("\r", "");
        text = text.replaceAll("", "");
        textCorrectTask.setText(text);
        this.textCorrectTaskMapper.insert(textCorrectTask);
        String taskId = textCorrectTask.getCorrectTaskId();
        //得到分段后结果
        List<CorrectionUtil.TextSegment> textSegments = CorrectionUtil.splitTextV1(text);
        //保存纠错任务详情
        this.correctTaskDetailsService.batchSaveTask(taskId, textSegments);
        //更新用户剩余纠错字符数 todo 智能获取上下文的userid
        this.userCorrectionCharLimitService.userChar(1, text.length());
        CreateCorrectTaskResp resp = new CreateCorrectTaskResp();
        resp.setTaskId(taskId);
        int totalCount = 0;
        for (CorrectionUtil.TextSegment segment : textSegments) {
            if (!segment.getText().equals("\n") && !segment.getText().equals("\n\n") && !segment.getText().equals("<br/>")) {
                totalCount++;
            }
        }
        resp.setTotalCount(totalCount);
        return resp;
    }

    /**
     * 执行纠错任务
     * 定时任务兜底调用纠错
     */
    public void correctionTask() {
        log.info("开始执行定时任务");
        // 直接使用线程池的方法判断是否有空闲线程
        boolean hasAvailableThread = correctExecutor.getActiveCount() < correctExecutor.getMaxPoolSize();
        if (!hasAvailableThread) {
            log.debug("当前没有空闲的线程，无法进行文本纠错");
            return;
        }
        // 获取一下当前有没有待处理的任务
        CorrectTaskDetails unParsedTask = this.correctTaskDetailsService.getUnParsedTask();
        if (ObjectUtils.isEmpty(unParsedTask)) {
            log.debug("当前没有待处理的任务");
            return;
        }
        //开始进行纠错处理
        correctExecutor.execute(() -> {
            try {
                String s = unParsedTask.getSentence().replaceAll(" ", "");
                s = s.replaceAll("\r", "");
                unParsedTask.setSentence(s);
                //todo 2025 0508 特殊句子处理
                //纠错处理 调用三方大模型
                correction(unParsedTask);
                unParsedTask.setState(CorrectState.SUCCESS);
            } catch (Exception e) {
                log.error("文本纠错定时任务异常", e);
                log.error("文本纠错定时任务异常：{}", e.getMessage());
                unParsedTask.setState(CorrectState.FAIL);
            } finally {
                //释放线程 线程池自己做
                if (!this.correctTaskDetailsService.updateById(unParsedTask)) {
                    log.error("修改任务状态失败:taskDetailsId:{}", unParsedTask.getId());
                }
                // 检查当前任务是否已经解析完了
                boolean finished = this.correctTaskDetailsService.checkTaskFinished(unParsedTask.getTaskId());
                if (finished) {
                    // 修改任务状态为已完成 .....无语
                    this.textCorrectTaskMapper.finishTask(unParsedTask.getTaskId());
                }
            }
        });
    }

    /**
     * 文本纠错处理
     *
     * @param unParsedTask 待处理的任务
     */
    private void correction(CorrectTaskDetails unParsedTask) {
        //todo 用户可以手动配置大模型进行任务处理 需要数据库获取配置？
        if (!aiServiceFactory.hasEnabledServices()) {
            throw new BusinessException("未启用任何文本纠错服务");
        }
        List<List<TextCorrectionMatchResp>> correctResults = new ArrayList<>();
        List<Long> localDictIds = new ArrayList<>();
        String taskId = unParsedTask.getTaskId();
        //拿到已开启的服务
        Map<CorrectionModel, AiCorrectionService> correctionServiceMap = aiServiceFactory.getEnabledServices();
        //遍历分别调用，根据优先级聚合结果集
        correctionServiceMap.forEach((model, service) -> {
            log.info("原文本：{}", unParsedTask.getSentence());
            //文本预处理（去除<br/>、敏感词替换）
            String text = preprocessText(unParsedTask);
            log.info("预处理后文本：{}", text);
//            if(model==CorrectionModel.LOCAL){
//                return;//事先做过本地词库的替换如果是本地直接跳过
//            }
            text = correctionDictService.localWordReplace(text);
            log.info("本地词库处理后AI纠错前文本：{}", text);
            String correctionText = service.correction(text, taskId);
            log.info("AI纠错处理后文本：{}", correctionText);
            String correctionTextV2 = "";
            if (StringUtils.hasText(correctionText)) {
                correctionTextV2 = this.correctionDictService.sensitiveWordPlace(correctionText, true);
            }
            boolean isPlaceWord = !correctionText.equals(correctionTextV2);
            if (isPlaceWord) {
                correctionText = correctionTextV2;
            }
            String category = null;
            String level = null;
            String source = null;
            if (model == CorrectionModel.LOCAL) {
                String beforeDash = CorrectionUtil.removeAfterDash(correctionText);
                level = "一级";
                localDictIds.addAll(CorrectionUtil.getAfterDash(correctionText));
                correctionText = beforeDash;
            } else {
                // 按照'----'分割字符串
                List<String> cList = Splitter.on("----").splitToList(correctionText);
                correctionText = getElementAtIndex(cList, 0);
                category = getElementAtIndex(cList, 1);
                level = getElementAtIndex(cList, 2);
                source = getElementAtIndex(cList, 3);
                // 如果进行了替换字符串，那么则认为是一级
                if (isPlaceWord) {
                    level = "一级";
                }
            }
            List<TextCorrectionMatchResp> textCorrectionMatchResps = TextCorrectMatch.obscureTextV1(model, unParsedTask.getSentence(), correctionText, category, source, level);
            if (!CollectionUtils.isEmpty(textCorrectionMatchResps)) {
                //每个ai得到的结果加入结果集
                correctResults.add(textCorrectionMatchResps);
            }
        });
        log.info("localDictIds:{}", localDictIds);
        if (CollectionUtils.isEmpty(localDictIds)) {
            localDictIds.add(-1L);
        }
        //得到模型优先级
        Map<CorrectionModel, Integer> modelPriority = aiCorrectionConfig.getModelPriority();
        //得到本地词典
        List<CorrectionDict> correctionDicts = this.correctionDictService.listByIds(localDictIds);
        //开始处理得到的结果集合并相同错误得到不同ai得到的不同错误
        //提前构建词典id到源的映射避免后续循环查找
        Map<Long, String> dictIdToSourceMap = correctionDicts.stream().collect(Collectors.toMap(CorrectionDict::getId, CorrectionDict::getSource));
        // 使用哈希表快速定位相同位置的错误，避免嵌套循环
        int localModelIndex = 0;
        Map<PositionKey, TextCorrectionMatchResp> positionToMatchMap = new HashMap<>();
        for (List<TextCorrectionMatchResp> correctResult : correctResults) {
            for (TextCorrectionMatchResp currentMatch : correctResult) {
                PositionKey positionKey = new PositionKey(currentMatch.getErrorStartIndex(), currentMatch.getErrorEndIndex(), currentMatch.getErrorText());
                //哈希表查找如果查到代表有相同错误
                TextCorrectionMatchResp existingMatch = positionToMatchMap.get(positionKey);
                if (existingMatch == null) {
                    //新增结果 并且要更新localModelIndex的值为方法内修改后的值避免值传递影响后续结果
                    localModelIndex = processAndStoreMatchResult(localDictIds, dictIdToSourceMap, localModelIndex, (Map<PositionKey, TextCorrectionMatchResp>) positionToMatchMap, currentMatch, positionKey);
                } else {
                    int prePriority = getPriority(existingMatch);
                    int curPriority = getPriority(currentMatch);
                    if (curPriority > prePriority) {
                        //当前优先级更改替换原来的
                        localModelIndex = processAndStoreMatchResult(localDictIds, dictIdToSourceMap, localModelIndex, (Map<PositionKey, TextCorrectionMatchResp>) positionToMatchMap, currentMatch, positionKey);
                    }
                }
            }
        }
        List<TextCorrectionMatchResp> diffList = new ArrayList<>(positionToMatchMap.values());
        //精准差异位置
        List<TextCorrectionMatchResp> newResults = exactPositionDiff(diffList);
        unParsedTask.setResult(newResults);
        this.correctTaskDetailsService.updateById(unParsedTask);//保存数据库完成
    }

    /**
     * 批量处理文本纠错结果，计算差异并设置详细的错误和修正信息
     * 使用谷歌diff插件进行字符串差异比较
     */
    private List<TextCorrectionMatchResp> exactPositionDiff(List<TextCorrectionMatchResp> matches) {
        List<TextCorrectionMatchResp> results = new ArrayList<>(matches.size());
        for (TextCorrectionMatchResp match : matches) {
            // 使用谷歌diff插件进行字符串差异比较
            Patch<String> diff = DiffUtils.diffInline(match.getErrorText(), match.getCorrectText());
            List<AbstractDelta<String>> deltas = diff.getDeltas();

            if (!CollectionUtils.isEmpty(deltas)) {
                for (AbstractDelta<String> delta : deltas) {
                    // 错误源
                    Chunk<String> source = delta.getSource();
                    // 更改目标
                    Chunk<String> target = delta.getTarget();
                    // 如果源和目标都为空，则跳过
                    if (CollectionUtils.isEmpty(source.getLines()) && CollectionUtils.isEmpty(target.getLines())) {
                        continue;
                    }
                    // 赋值关键位置/字符信息
                    match.setErrorChar(CollectionUtils.isEmpty(source.getLines()) ? "" : source.getLines().get(0));
                    match.setErrorCharPosition(source.getPosition());
                    match.setCorrectChar(CollectionUtils.isEmpty(target.getLines()) ? "" : target.getLines().get(0));
                    match.setCorrectCharPosition(target.getPosition());
                }
            }
            results.add(match);
        }
        return results;
    }

    /**
     * 更新结果集如果是本地词典并设置来源
     *
     * @param localDictIds       本地词典id
     * @param dictIdToSourceMap  本地词典id到来源的映射
     * @param localModelIndex    本地词典索引
     * @param positionToMatchMap
     * @param currentMatch
     * @param positionKey
     * @return
     */
    private int processAndStoreMatchResult(List<Long> localDictIds, Map<Long, String> dictIdToSourceMap, int localModelIndex, Map<PositionKey, TextCorrectionMatchResp> positionToMatchMap, TextCorrectionMatchResp currentMatch, PositionKey positionKey) {
        positionToMatchMap.put(positionKey, currentMatch);
        if (isLocalModel(currentMatch) && localModelIndex < localDictIds.size()) {
            Long dictId = localDictIds.get(localModelIndex++);
            String source = dictIdToSourceMap.get(dictId);
            if (StringUtils.hasText(source)) {
                currentMatch.setLinkSource(source);
            }
        }
        return localModelIndex;
    }

    /**
     * 获取纠错结果
     *
     * @param taskId 纠错任务id
     * @return
     */
    public CorrectTaskResp getCorrectTask(String taskId) {
        //判断任务是否存在
        TextCorrectTask correctTask = this.textCorrectTaskMapper.selectById(taskId);
        if (ObjectUtils.isNull(correctTask)) {
            throw new BusinessException("网络异常稍后再试");
        }
        LambdaQueryWrapper<CorrectTaskDetails> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CorrectTaskDetails::getTaskId, taskId);
        queryWrapper.eq(CorrectTaskDetails::getState, CorrectState.SUCCESS);
        queryWrapper.orderByAsc(CorrectTaskDetails::getSort);
        List<CorrectTaskDetails> correctTaskDetails = this.correctTaskDetailsService.list(queryWrapper);
        List<CorrectTaskDetails> list = new ArrayList<>();
        for (int i = 0; i < correctTaskDetails.size(); i++) {
            CorrectTaskDetails taskDetails = correctTaskDetails.get(i);
            //判断是否是连续的句子
            if (taskDetails.getSort() != i + 1) {
                break;
            }
            //判断结果集是否为空
            if (ObjectUtils.isEmpty(taskDetails.getResult())) {
                list.add(correctTaskDetails.get(i));
            }
        }
        List<CorrectTaskDetails> taskDetailsListV2 = new ArrayList<>();
        for (CorrectTaskDetails taskDetail : correctTaskDetails) {
            List<TextCorrectionMatchResp> correctionMatchResult = taskDetail.getResult();
            List<TextCorrectionMatchResp> newResults = new ArrayList<>();
            for (TextCorrectionMatchResp matchResp : correctionMatchResult) {
                //只取3级的错误
                if (!matchResp.getLevel().equals("三级") && matchResp.getErrorText().contains("</error-text>")) {
                    newResults.add(matchResp);
                }
            }
            if (!CollectionUtils.isEmpty(newResults)) {
                taskDetail.setResult(newResults);
                taskDetailsListV2.add(taskDetail);
            }
        }
        log.info("连续句子:{}", JSON.toJSONString(list));
        List<CorrectTaskDetailsResp> details = SmartPageUtil.convert(taskDetailsListV2, CorrectTaskDetailsResp.class);
        for (CorrectTaskDetailsResp detail : details) {
            List<TextCorrectionMatchResp> result = detail.getResult();
            if (CollectionUtils.isEmpty(result)) {
                break;
            }
            log.info("新结果集:{}", JSON.toJSONString(result));
            //将句子顺序排序
            if (result.size() > 1) {
                result.sort(Comparator.comparingInt(TextCorrectionMatchResp::getErrorStartIndex));
            }
            detail.setResult(result);
        }
        CorrectTaskResp correctTaskResp = SmartPageUtil.convert(correctTask, CorrectTaskResp.class);
        correctTaskResp.setCorrectResult(details);
        LambdaQueryWrapper<CorrectTaskDetails> Wrapper = new LambdaQueryWrapper<>();
        Wrapper.eq(CorrectTaskDetails::getTaskId, taskId);
        Wrapper.eq(CorrectTaskDetails::getState, CorrectState.SUCCESS);
        Wrapper.ne(CorrectTaskDetails::getSentence, "<br/>");
        correctTaskResp.setFinishCount(this.correctTaskDetailsService.count(Wrapper));
        return correctTaskResp;
    }

    /**
     * 匹配文本错误开始结束封装的类，便于哈希表中访问查找
     */
    private record PositionKey(int startIndex, int endIndex, String errorText) {

    }

    // 文本预处理（去除<br/>、敏感词替换）
    private String preprocessText(CorrectTaskDetails task) {
        String text = task.getSentence();
        if (text.endsWith("<br/>")) {
            text = text.substring(0, text.length() - 5);
        }
        return correctionDictService.sensitiveWordPlace(text, false);
    }

    /**
     * 判断是不是本地词典
     *
     * @param match
     * @return
     */
    private boolean isLocalModel(TextCorrectionMatchResp match) {
        return match.getModelSource().equals(CorrectionModel.LOCAL);
    }

    // 获取模型优先级
    private int getPriority(TextCorrectionMatchResp match) {
        return aiCorrectionConfig.getPriorityByCode(match.getModelSource());
    }

    public static String getElementAtIndex(List<String> list, int index) {
        if (index >= 0 && index < list.size()) {
            return list.get(index);
        } else {
            return null;
        }
    }
}
